package fr.oneview.mapping.csv;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.text.WordUtils;

import com.androwit.engine.mapping.BasicMappingField;
import com.androwit.engine.mapping.Converter;
import com.androwit.engine.mapping.MappingField;
import com.androwit.engine.mapping.MappingFieldsDefiner;
import com.androwit.engine.mapping.converter.str.BooleanConverter;
import com.androwit.engine.mapping.converter.str.FloatConverter;
import com.androwit.engine.mapping.converter.str.IntegerConverter;

/**
 * Utility class to help developers get started: generates classes from a sample csv file. Generated classse are
 * 
 * <ul>
 * <li>the pojo that will hold the data
 * <li>the {@link MappingField} definitions
 * <li>the {@link MappingFieldsDefiner}
 * </ul>
 * 
 * @author Zied Hamdi - http://1vu.fr
 * 
 */
@SuppressWarnings("rawtypes")
public class DevUtil {

	public static String generateBeanClass(BasicCsvReader csvReader) throws IOException {
		Map<String, BasicMappingField> metaData = analyseFieldsFromSampleFile(csvReader);
		String[] headerTitles = csvReader.getHeaderTitles();
		return generateBeanClass(metaData, headerTitles);
	}

	public static String generateBeanClass(Map<String, BasicMappingField> metaData, String[] headerTitles) {
		StringBuffer classBuff = new StringBuffer("public class CsvData {\n");
		for (String column : headerTitles) {
			BasicMappingField basicMappingField = metaData.get(column);
			String name = basicMappingField.getName();
			Converter converter = basicMappingField.getConverter();
			String type = beanTypeFromConverter(converter);
			classBuff.append("\tprotected ");
			classBuff.append(type);
			classBuff.append(" ");
			classBuff.append(name);
			classBuff.append(";\n");
		}
		classBuff.append("}");
		return classBuff.toString();
	}

	public static String beanTypeFromConverter(Converter converter) {
		String type = converter == null ? "String" : (converter instanceof FloatConverter ? "Float" : (converter instanceof IntegerConverter ? "Integer"
				: "Boolean"));
		return type;
	}

	public static String generateDefinitionClass(Map<String, BasicMappingField> metaData, String[] headerTitles) {
		StringBuffer classBuff = new StringBuffer();
		classBuff.append("import static fr.oneview.mapping.csv.CsvFieldsDefiner.Fields.*;\r\n");
		classBuff.append("@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\r\n");
		classBuff.append("public class CsvFieldsDefiner extends BasicMappingFieldsDefiner {\n");
		classBuff.append("\tprotected static interface Fields {\n");
		for (String column : headerTitles) {
			BasicMappingField basicMappingField = metaData.get(column);
			String name = basicMappingField.getName();
			Converter converter = basicMappingField.getConverter();
			classBuff.append("\t\tBasicMappingField ");
			classBuff.append(column);
			classBuff.append(" = new BasicMappingField( \"");
			classBuff.append(column);
			classBuff.append("\", ");
			classBuff.append(converter == null ? "null" : "new " + converter.getClass().getSimpleName() + "()");
			classBuff.append(", \"");
			classBuff.append(name);
			classBuff.append("\" );\n");
		}
		classBuff.append("\t}\n");
		classBuff.append("\tprotected static BasicMappingField[] FIELDS = {\n\t\t");
		for (String column : headerTitles) {
			classBuff.append(column);
			classBuff.append(", ");
		}
		classBuff.append("\n\t};\n");

		classBuff.append("\n\tpublic CsvFieldsDefiner() {");
		classBuff.append("\n\t\tsuper( CsvData.class, FIELDS );");
		classBuff.append("\n\t}");
		classBuff.append("\n}");
		return classBuff.toString();
	}

	public static Map<String, BasicMappingField> analyseFieldsFromSampleFile(BasicCsvReader csvReader) throws IOException {
		// System.out.println("Column headers found:" + Arrays.asList(csvReader.headerTitles));

		// next line
		csvReader.readRecord();
		Map<String, String> firstDataRow = csvReader.readLine();
		Map<String, BasicMappingField> metaData = new HashMap<String, BasicMappingField>();
		Set<String> headers = firstDataRow.keySet();
		for (String column : headers) {
			String javaName = DevUtil.asJavaProperty(column);
			BasicMappingField field = new BasicMappingField(column, null, javaName);
			metaData.put(column, field);
			String value = firstDataRow.get(column);
			try {
				Float.parseFloat(value);
				try {
					Integer.parseInt(value);
					field.setConverter(new IntegerConverter());
				} catch (Exception ex) {
					field.setConverter(new FloatConverter());
				}
			} catch (Exception ex) {
				if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false"))
					field.setConverter(new BooleanConverter("true", "false"));
				if (value.equalsIgnoreCase("y") || value.equalsIgnoreCase("n"))
					field.setConverter(new BooleanConverter("y", "n"));

			}
			// System.out.println("value: " + value);
			// System.out.println("analysed field: " + field);
		}
		return metaData;
	}

	public static String asJavaProperty(String column) {
		String javaName;
		if (column.length() == 1)
			javaName = column.toLowerCase();
		else {
			javaName = WordUtils.capitalizeFully(column.replaceAll("_", " "));
			javaName = javaName.substring(0, 1).toLowerCase() + javaName.substring(1).replaceAll(" ", "");
		}
		return javaName;
	}

	public static void main(String[] args) throws IOException {
		System.out
				.println("Usage\n \t java "
						+ BasicCsvReader.class.getSimpleName()
						+ " [csvfilePath (complete path)] [csvSeparator (char): optional (defaults to tab)] [skipLines (number) : optional (possible only if param 2 is passed)]");
		String csvFile = args[0];
		char delimiter = args.length > 1 ? args[1].charAt(0) : '\t';
		BasicCsvReader csvReader = new BasicCsvReader(csvFile, delimiter, 1);
		Map<String, BasicMappingField> metaData = analyseFieldsFromSampleFile(csvReader);
		String generatedPojo = generateBeanClass(metaData, csvReader.headerTitles);
		System.out.println("Data object:\n" + generatedPojo);
		String generatedDef = generateDefinitionClass(metaData, csvReader.headerTitles);
		System.out.println("Definition object:\n" + generatedDef);
		csvReader.close();
	}

}
